<?php
/**
 * 基于ThinkAdmin框架的智能门禁设备TCP服务器
 */

// 设置正确的根路径
$rootPath = dirname(__DIR__, 2);
chdir($rootPath);

// 引入ThinkAdmin框架
require_once $rootPath . '/vendor/autoload.php';

// 使用ThinkAdmin标准初始化方式
try {
    echo "Initializing ThinkAdmin framework...\n";
    \think\admin\service\RuntimeService::doConsoleInit();
    echo "ThinkAdmin framework initialized successfully\n";
} catch (\Throwable $e) {
    echo "Failed to initialize ThinkAdmin framework: " . $e->getMessage() . "\n";
    echo "Stack trace: " . $e->getTraceAsString() . "\n";
    exit(1);
}

// 设置运行环境
error_reporting(E_ALL);
ini_set('display_errors', 'on');
set_time_limit(0);

// 服务器配置
$host = '0.0.0.0';
$port = 8778;

// 协议常量
$marker = 'OFZL';
$version = 0x02;
$dtype = 0x01;

// 日志目录
$logDir = __DIR__ . '/logs';
if (!is_dir($logDir)) @mkdir($logDir, 0777, true);

/**
 * 记录日志
 */
function logMsg(string $msg): void {
    $file = __DIR__ . '/logs/' . date('Y-m-d') . '.log';
    $line = '[' . date('Y-m-d H:i:s') . '] ' . $msg . "\n";
    file_put_contents($file, $line, FILE_APPEND);
    echo $line;
}

/**
 * 智能检测字节序并解析消息头
 */
function parseHeader(string $data): ?array {
    if (strlen($data) < 11) return null;
    
    $marker = substr($data, 0, 4);
    if ($marker !== 'OFZL') return null;
    
    $version = ord($data[4]);
    $dataType = ord($data[5]);
    $cmd = ord($data[6]);
    
    // 尝试大端序
    $lengthBig = unpack('N', substr($data, 7, 4))[1];
    // 尝试小端序
    $lengthLittle = unpack('V', substr($data, 7, 4))[1];
    
    // 智能判断字节序（合理的长度应该在1-10MB之间）
    if ($lengthBig > 0 && $lengthBig < 10485760) {
        return ['marker' => $marker, 'version' => $version, 'dataType' => $dataType, 'cmd' => $cmd, 'length' => $lengthBig, 'endian' => 'big'];
    } elseif ($lengthLittle > 0 && $lengthLittle < 10485760) {
        return ['marker' => $marker, 'version' => $version, 'dataType' => $dataType, 'cmd' => $cmd, 'length' => $lengthLittle, 'endian' => 'little'];
    }
    
    return null;
}

/**
 * 构建响应消息
 */
function buildResponse(int $cmd, array $data): string {
    global $marker, $version, $dtype;
    
    $json = json_encode($data, JSON_UNESCAPED_UNICODE);
    $length = strlen($json);
    
    // 使用大端序构建响应
    $header = $marker . chr($version) . chr($dtype) . chr($cmd) . pack('N', $length);
    return $header . $json;
}

/**
 * 处理设备登录
 */
function handleLogin(?array $data, string $peer): array {
    if (!$data) {
        return [
            'result' => 400,
            'desc' => '请求数据格式错误',
            'message' => 'loginRsp',
            'messageId' => '',
            'data' => []
        ];
    }
    
    $sn = strval($data['sn'] ?? '');
    $messageId = strval($data['messageId'] ?? '');
    
    logMsg("[LOGIN] Device {$sn} login attempt from {$peer}, messageId: {$messageId}");
    
    try {
        // 使用ThinkAdmin模型查询设备
        $device = \app\checkin\model\Device::mk()->where(['sn' => $sn])->findOrEmpty();
        
        if ($device->isEmpty()) {
            // 设备不存在，自动注册
            $device = \app\checkin\model\Device::mk()->save([
                'sn' => $sn,
                'name' => "设备_{$sn}",
                'store_id' => 1, // 默认门店
                'passwd' => md5($sn . time()),
                'status' => 1,
                'last_online' => date('Y-m-d H:i:s'),
                'create_time' => date('Y-m-d H:i:s'),
                'update_time' => date('Y-m-d H:i:s')
            ]);
            logMsg("[LOGIN] Device {$sn} auto-registered");
        } else {
            // 更新最后在线时间
            $device->save(['last_online' => date('Y-m-d H:i:s')]);
        }
        
        logMsg("[LOGIN] Device {$sn} login SUCCESS");
        
        return [
            'result' => 200,
            'desc' => 'Success',
            'message' => 'loginRsp',
            'messageId' => $messageId,
            'data' => []
        ];
        
    } catch (\Throwable $e) {
        logMsg("[LOGIN] Error: " . $e->getMessage());
        return [
            'result' => 500,
            'desc' => '登录失败',
            'message' => 'loginRsp',
            'messageId' => $messageId,
            'data' => []
        ];
    }
}

/**
 * 处理心跳请求
 */
function handleHeartbeat(?array $data, string $peer): array {
    if (!$data) {
        return [
            'result' => 400,
            'desc' => '请求数据格式错误',
            'message' => 'heartbeatRsp',
            'messageId' => '',
            'data' => []
        ];
    }
    
    $sn = strval($data['sn'] ?? '');
    $messageId = strval($data['messageId'] ?? '');
    $peopleCount = intval($data['data']['peopleCount'] ?? 0);
    $capacity = intval($data['data']['capacity'] ?? 0);
    
    logMsg("[HEARTBEAT] Device {$sn} heartbeat from {$peer}, messageId: {$messageId}, people: {$peopleCount}/{$capacity}");
    
    try {
        // 更新设备状态
        \app\checkin\model\Device::mk()->where(['sn' => $sn])->update([
            'last_online' => date('Y-m-d H:i:s'),
            'people_count' => $peopleCount,
            'capacity' => $capacity
        ]);
        
        return [
            'result' => 200,
            'desc' => 'Success',
            'message' => 'heartbeatRsp',
            'messageId' => $messageId,
            'data' => []
        ];
        
    } catch (\Throwable $e) {
        logMsg("[HEARTBEAT] Error: " . $e->getMessage());
        return [
            'result' => 500,
            'desc' => '心跳处理失败',
            'message' => 'heartbeatRsp',
            'messageId' => $messageId,
            'data' => []
        ];
    }
}

/**
 * 处理获取所有用户请求
 */
function handleGetAllUsers(?array $data, string $peer): array {
    if (!$data) {
        return [
            'result' => 400,
            'desc' => '请求数据格式错误',
            'message' => 'allUserRsp',
            'messageId' => '',
            'data' => []
        ];
    }
    
    $sn = strval($data['sn'] ?? '');
    $messageId = strval($data['messageId'] ?? '');
    
    logMsg("[GET_ALL_USERS] Device {$sn} requesting user list from {$peer}, messageId: {$messageId}");
    
    try {
        // 获取设备所属门店
        $device = \app\checkin\model\Device::mk()->where(['sn' => $sn])->findOrEmpty();
        $storeId = $device->isEmpty() ? 1 : $device->getAttr('store_id');
        
        logMsg("[GET_ALL_USERS] Device {$sn} belongs to store {$storeId}");
        
        // 获取该门店的所有启用会员
        $members = \app\checkin\model\Member::mk()
            ->where(['register_store_id' => $storeId, 'status' => 1])
            ->field('id,name,phone,img,card_no,update_time')
            ->select()
            ->toArray();
        
        logMsg("[GET_ALL_USERS] Found " . count($members) . " members for store {$storeId}");
        
        $userIds = [];
        foreach ($members as $member) {
            $userIds[] = [
                'userId' => strval($member['id']),
                'name' => $member['name'] ?? '',
                'phone' => $member['phone'] ?? '',
                'cardNo' => $member['card_no'] ?? '',
                'faceImg' => $member['img'] ?? '',
                'updateTime' => $member['update_time'] ?? ''
            ];
            logMsg("[GET_ALL_USERS] Member: {$member['name']} (ID: {$member['id']})");
        }
        
        return [
            'result' => 200,
            'desc' => 'Success',
            'message' => 'allUserRsp',
            'messageId' => $messageId,
            'data' => [
                'userIds' => $userIds,
                'updateDate' => date('Y-m-d H:i:s')
            ]
        ];
        
    } catch (\Throwable $e) {
        logMsg("[GET_ALL_USERS] Error: " . $e->getMessage());
        return [
            'result' => 500,
            'desc' => '获取用户列表失败',
            'message' => 'allUserRsp',
            'messageId' => $messageId,
            'data' => [
                'userIds' => [],
                'updateDate' => date('Y-m-d H:i:s')
            ]
        ];
    }
}

/**
 * 处理消息
 */
function handleMessage(int $cmd, ?array $data, string $peer): array {
    switch ($cmd) {
        case 0x01: // 登录请求
            return handleLogin($data, $peer);
        case 0x07: // 心跳请求
            return handleHeartbeat($data, $peer);
        case 0x0B: // 获取所有用户ID请求
            return handleGetAllUsers($data, $peer);
        default:
            return [
                'result' => 200,
                'desc' => 'Success',
                'message' => 'unknownRsp',
                'messageId' => strval($data['messageId'] ?? ''),
                'data' => []
            ];
    }
}

// 启动服务器
logMsg("ThinkAdmin TCP Server starting on tcp://{$host}:{$port}");

try {
    $socket = stream_socket_server("tcp://{$host}:{$port}", $errno, $errstr);
    if (!$socket) {
        throw new Exception("Failed to create server: {$errstr} ({$errno})");
    }
    
    logMsg("ThinkAdmin TCP Server started successfully");
    
    while (true) {
        $client = stream_socket_accept($socket, -1, $peer);
        if (!$client) continue;
        
        logMsg("Accepted connection from {$peer}");
        
        // 处理客户端连接
        while (!feof($client)) {
            try {
                // 读取消息头
                $headerData = fread($client, 11);
                if (strlen($headerData) < 11) break;
                
                $header = parseHeader($headerData);
                if (!$header) {
                    logMsg("Invalid header from {$peer}");
                    break;
                }
                
                logMsg("Header from {$peer}: marker={$header['marker']}, cmd=0x{$header['cmd']}, len={$header['length']} ({$header['endian']}-endian)");
                
                // 读取消息体
                $bodyData = '';
                $remaining = $header['length'];
                while ($remaining > 0 && !feof($client)) {
                    $chunk = fread($client, min($remaining, 8192));
                    if ($chunk === false) break;
                    $bodyData .= $chunk;
                    $remaining -= strlen($chunk);
                }
                
                if (strlen($bodyData) !== $header['length']) {
                    logMsg("Incomplete message from {$peer}");
                    break;
                }
                
                // 解析JSON数据
                $data = json_decode($bodyData, true);
                logMsg("Message from {$peer}: " . $bodyData);
                
                // 处理消息
                $response = handleMessage($header['cmd'], $data, $peer);
                
                // 构建响应
                $responseCmd = $header['cmd'] + 1; // 响应命令 = 请求命令 + 1
                $responseData = buildResponse($responseCmd, $response);
                
                // 发送响应
                fwrite($client, $responseData);
                logMsg("Response sent to {$peer}: " . strlen($responseData) . " bytes, cmd=0x" . dechex($responseCmd) . ", json=" . json_encode($response, JSON_UNESCAPED_UNICODE));
                
            } catch (\Throwable $e) {
                logMsg("Error handling client {$peer}: " . $e->getMessage());
                break;
            }
        }
        
        fclose($client);
        logMsg("Connection closed: {$peer}");
    }
    
} catch (\Throwable $e) {
    logMsg("Server error: " . $e->getMessage());
}
?>
